"""
    Contains accompanies classes for LookupController.
"""

import cherrypy
from sqlobject.sqlbuilder import SQLObjectField, _LikeQuoted, SQLExpression, AND, OR, STARTSWITH, NOT

from mnemoply.utils import Wrapper, parsing_between
from mnemoply.model import __connection__
from turbogears.identity import current
from mnemoply._renewed_utils import get_filter_by_user


class TableCol:
    def __init__(self, **kw):
        self.__dict__.update(kw)

class SQLObjectFieldLower(SQLObjectField):
    def __sqlrepr__(self, db):
        return "lower(%s.%s)" % (self.tableName, self.fieldName)

class LookupRequest:
    def __init__(self, filters,filter_by_user = None, order_by=None, order_asc=True):
        self.filters = filters
        self.order_by = order_by
        self.order_asc = order_asc
        self.filter_by_user = filter_by_user

class SQLPassthru(SQLExpression):
    def __init__(self, s):
        self.s = s

    def __sqlrepr__(self, db):
        return self.s

class SQLObjectLookupDataSource(object):
    """
        Datasource as wrapper over model_object.select() request.
    """

    def __init__(self, cls):
        self.model = cls

    def _buildQuery(self, request):
        conditions = []

        anded = []
        for name, v in request.filters.iteritems():
            field = getattr(self.model.q, name)
            if v == '':
                anded.append(OR(
                    SQLPassthru("(LENGTH(%s)=0)" % field.__sqlrepr__(None)),
                    field.__eq__(None),
                ))
            elif v == '!~':
                anded.append(AND(
                    field.__ne__(None),
                    SQLPassthru("(LENGTH(%s)>0)" % field.__sqlrepr__(None)),
                ))
            elif v.startswith('='):
                anded.append(field.__eq__(v[1:]))
            elif v.startswith('>'):
                anded.append(field.__gt__(v[1:]))
            elif v.startswith('<'):
                anded.append(field.__lt__(v[1:]))
            elif v.startswith('!^'):
                anded.append(NOT(STARTSWITH(field, v[2:])))
            elif v.startswith('!'):
                anded.append(field.__ne__(v[1:]))
            elif v.startswith('^'):
                anded.append(STARTSWITH(field, v[1:]))
            else:
                anded.append(field.contains(v))
        if anded:
            conditions.append(AND(*anded))

        return self.model.select(
            orderBy=((request.order_asc and '-' or '') + request.order_by),
            *conditions
        )

    def getTotal(self, request):
        return int(self._buildQuery(request).count())

    def getData(self, request, start=None, end=None):
        data = self._buildQuery(request)

        if start or end:
            data = data[start:end]

        return data

class wdict(dict):
    __getattr__ = dict.__getitem__



class TableLookupDataSource(object):
    """
        Datasource from SQL view.
    """

    def __init__(self, table_name):
        self.table_name = table_name
        self.query_from_part = " FROM " + self.table_name

    def connection(self):
        return __connection__.getConnection()

    def _buildWhereChunks(self, request):
        connection = self.connection()
        chunks = []
        if len(request.filters) > 0:
            for col, v in request.filters.items():
                if v.startswith('='):
                    chunks.append("%s = %s" % (col, connection.sqlrepr(v[1:])))
                elif v == '!~':
                    chunks.append("((%s IS NOT NULL) AND (LENGTH(%s) > 0))" % (col, col))
                elif v.startswith('>'):
                    chunks.append("%s > %s" % (col, connection.sqlrepr(v[1:])))
                elif v.startswith('<'):
                    chunks.append("%s < %s" % (col, connection.sqlrepr(v[1:])))
                elif v.startswith('!^'):
                    chunks.append("NOT(%s LIKE %s)" % (col,
                        connection.sqlrepr(_LikeQuoted(v[2:]) + "%")))
                elif v.startswith('!'):
                    chunks.append("%s <> %s" % (col, connection.sqlrepr(v[1:])))
                elif v.startswith('^'):
                    chunks.append("%s LIKE %s" % (col,
                        connection.sqlrepr(_LikeQuoted(v[1:]) + "%")))
                elif v.count('&') > 0:    # using & for search like "foo" AND "bar"
                    split_words = v.split('&')
                    chunk = []
                    for word in split_words:
                        chunk.append("(%s LIKE %s)" % (col, connection.sqlrepr("%" + _LikeQuoted(word) + "%")))
                    chunks.append(" AND ".join(chunk))
                elif not len(v):
                    chunks.append("((LENGTH(%s) = 0) OR (%s IS NULL))" % (col, col))
                elif parsing_between(v) is not ({} or None):
                    start_date, end_date = parsing_between(v)
                    chunks.append("((%s > %s) AND (%s < %s))" % (col, connection.sqlrepr(start_date), col,  connection.sqlrepr(end_date)))
                else:
                    chunks.append("%s LIKE %s" % (col,
                        connection.sqlrepr("%" + _LikeQuoted(v) + "%")))
        return chunks
   

    def _buildWhere(self, request):
        user = current.user
        filter_by_user = request.filter_by_user
        
        query = ""
        chunks = self._buildWhereChunks(request)
        if filter_by_user:
            chunks +=[filter_by_user]
        if len(chunks) > 0:
            query += " WHERE "
            query += " AND ".join(chunks)
        return query

    def _buildOrder(self, request):
        query = ""
        if request.order_by is not None:
            query = " ORDER BY " + request.order_by + (request.order_asc and " ASC" or " DESC")
        return query

    def getTotal(self, request):
        query = "SELECT COUNT(*) %s%s" % (self.query_from_part, self._buildWhere(request))
        return int(self.connection().queryOne(query)[0])

    def getData(self, request, start=None, end=None):
        connection = self.connection()

        query = "SELECT * %s%s%s" % (
            self.query_from_part,
            self._buildWhere(request),
            self._buildOrder(request)
        )

        if start or end:
            query = connection._queryAddLimitOffset(query, start, end)

        result = []
        # That's a hack since Transaction pass queryAllDescription right to
        # Connection.runInTransaction instead of running in current transaction
        descr, rows = connection._dbConnection._queryAllDescription(
            connection._connection,
            query,
        )

        for row in list(rows):
            row_dict = wdict([(row_desc[0], row[num]) for num, row_desc in enumerate(descr)])
            result.append(row_dict)
        return result

class VisibleColumns(object):
    """
        UI columns in lookups.
    """

    def __init__(self, session_data, lookup_controller):
        if 'visible_columns' in session_data:
            if len(session_data.visible_columns.strip()) > 0:
                nums = [int(x) for x in session_data.visible_columns.split('.')]
            else:
                nums = []
            self.cols = [lookup_controller.columns[n] for n in nums]
        else:
            self.cols = [x for x in lookup_controller.columns
                if not hasattr(x, 'hidden') or not x.hidden]

        self.session_data = session_data
        self.lookup_controller = lookup_controller

    def _save(self):
        """
            Save cureent columns order to session.
        """
        vc = '.'.join([str(self.lookup_controller.columns.index(x)) for x in self.cols])
        self.session_data['visible_columns'] = vc

    def add(self, col):
        """
            Add columns to column list.
        """

        if col not in self.cols:
            self.cols.append(col)
        self._save()

    def remove(self, col):
        """
            Remove column from column list.
        """

        if col in self.cols:
            del self.cols[self.cols.index(col)]
        self._save()

    def clear(self):
        """
            Erase all columns.
        """

        self.cols = []
        self._save()

    def set(self, column_list):
        """
            Set columns with `column_list`.
        """

        self.cols = column_list
        self._save()

    def move_right(self, col_index):
        """
            Move column with `col_index` right.
        """

        if col_index < len(self.cols) - 1:
            (self.cols[col_index], self.cols[col_index + 1]) = (self.cols[col_index + 1], self.cols[col_index])
        self._save()

    def move_left(self, col_index):
        """
            Move column with `col_index` left.
        """

        if col_index > 0:
            (self.cols[col_index - 1], self.cols[col_index]) = (self.cols[col_index], self.cols[col_index - 1])
        self._save()

class LookupData(Wrapper):
    """
        Dictionary wrapper with information about lookup state.
    """

    def __init__(self, controller):
        self.__dict__['controller'] = controller
        self.__dict__['controller_name'] = controller.__class__.__name__

        cherrypy.session.acquire_lock()
        self.__dict__['all_lookup_data'] = cherrypy.session.get('lookup', {})
        data = self.all_lookup_data.get(self.controller_name, {})

        Wrapper.__init__(self, data)

    def clear(self):
        """
            Erase all data from themself and save empty state.
        """

        Wrapper.clear(self)
        self.save()

    def save(self):
        """
            Put all data to cherrypy session.
        """

        self.all_lookup_data[self.controller_name] = self.get_dict()
        cherrypy.session['lookup'] = self.all_lookup_data

    def empty(self):
        return len(self.items()) == 0

    def update(self, args):
        """
            Works with current state.
            For different actions use different args.
        """

        if 'clear' in args:
            self.clear()
        if 'sort' in args:
            self.order_by = args.sort
        if 'asc' in args:
            self.order_asc = int(args['asc']) == 1

        if 'clear_filters' in args:
            self.filters = {}

        if 'page' in args:
            self.current_page = int(args.page)

        if 'per_page' in args:
            per_page = int(args['per_page'])
            if per_page > 0:
                self.per_page = per_page

         
        found_filters = False
        for k in args.keys():
            if k.startswith('flt_'):
                found_filters = True

        if found_filters:
            filters = {}

            for col in self.controller.columns:
                if 'flt_' + col.name in args:
                    v = args['flt_' + col.name]
                    if len(v) > 0:
                        if v == '~':
                            # '~' means empty string
                            filters[col.name] = ''
                        else:
                            if v[0] in '!><=':
                                # Check argument format
                                arg = v[1:]
                                if hasattr(col, 'validator'):
                                    try:
                                        arg = str(col.validator.to_python(arg))
                                    except:
                                        continue
                                v = v[0] + arg
                            filters[col.name] = v

            self.filters = filters

        visible_columns = VisibleColumns(session_data=self, lookup_controller=self.controller)

        #if 'delete_vc' in args:
        #    col = self.controller.columns[int(args.delete_vc)]
        #    visible_columns.remove(col)
        #
        #if 'add_vc' in args:
        #    col = self.controller.columns[int(args.add_vc)]
        #    visible_columns.add(col)
        if 'visible' in args:
            visible_columns.clear()
            for colnum in args.visible:
                col = self.controller.columns[int(colnum)]
                visible_columns.add(col)

        if 'move_right_vc' in args:
            visible_columns.move_right(int(args.move_right_vc))

        if 'move_left_vc' in args:
            visible_columns.move_left(int(args.move_left_vc))

        if 'column' in args:
            column_dict = dict([(col.name, col) for col in self.controller.columns])
            visible_column_list = [column_dict[col] for col in args['column']]
            visible_columns.set(visible_column_list)

        # Remove all hidden filters
        visible_col_names = [col.name for col in visible_columns.cols]
        filters = self.get('filters', {})

        self.filters = dict([
            (col_name, value) for col_name, value in filters.items()
            if col_name in visible_col_names
        ])

class LocalMenuItem():
    def __init__(self, params, widget, condition = None):
        self.params = params
        self.widget = widget
        self.condition = condition
        
    def isShown(self):
        return  getattr(self, 'condition', lambda: True)()


class LocalMenu(object):
    def __init__(self):
        # I know, this method would be called more than once. Dirty workaround.
        if not hasattr(self, 'local_menu'):
            self.local_menu = []
        # Container's object
        if not hasattr(self, 'object_local_menu'):
            self.object_local_menu = []
